/**
 * dbms-sjtu
 * QueryPlanner.java create @ ����9:41:29
 * @author zhangweipeng
 */
package dbms.planner;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dbms.Constants;
import dbms.Debug;
import dbms.absyn.ConditionList;
import dbms.absyn.ExpList;
import dbms.absyn.FromClause;
import dbms.absyn.SelectClause;
import dbms.absyn.SelectExp;
import dbms.absyn.WhereClause;
import dbms.metadata.TableManager;
import dbms.parse.Lexer;
import dbms.parse.parser;
import dbms.record.RecordPage;
import dbms.server.DbmsServer;

/**
 * @author zhangweipeng.pt
 */
public class QueryPlanner {
	
	public static String executeQuery(SelectExp selectData){
		Map<Integer, List<Object>> resultSet = new HashMap<Integer, List<Object>>();
		
		FromClause fromClause = selectData.from_stmt;
		if(fromClause.type == 1){
			//table name
			String tableName = fromClause.tableList.attr.attr.name;
			if(DbmsServer.masterPage.tableMap.containsKey(tableName)){
				resultSet = createPlan(selectData);
			} else if(DbmsServer.masterPage.viewMap.containsKey(tableName)){
				String viewSQL = DbmsServer.masterPage.viewMap.get(tableName).viewSQL;
				if(viewSQL.contains("SELECT")){
					viewSQL = viewSQL.substring(viewSQL.indexOf("SELECT"));
				} else {
					viewSQL = viewSQL.substring(viewSQL.indexOf("select"));
				}
				parser p = new parser(new Lexer(new StringReader(viewSQL)));
				try {
					SelectExp selectExp = (SelectExp)((ExpList)p.parse().value).exp;
					selectData.from_stmt.type = 3;
					selectData.from_stmt.exp = selectExp;
					resultSet = executePlan(selectData);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else {
				Debug.Error("This table " + tableName + " is not exist.");
			}
		} else if(fromClause.type == 3){
			//selectExp
			resultSet = executePlan(selectData);
		} else {
			//alias table name
		}
		
		String resultString = "";
		if(!resultSet.isEmpty()){
			Debug.P("Total record num is " + resultSet.size());
			for(Integer integer : resultSet.keySet()){
				System.out.print(integer + Constants.DBMS_IFN);
				for(Object object : resultSet.get(integer)){
					System.out.print(object + Constants.DBMS_IFN);
				}
				System.out.println();
			}
			/*resultString += "Total record num is " + resultSet.size() + "#";
			for(Integer integer : resultSet.keySet()){
				resultString += integer + Constants.DBMS_IFN;
				for(Object object : resultSet.get(integer)){
					resultString += object + Constants.DBMS_IFN;
				}
				resultString += "#";
			}*/
		}
		
		return resultString;
	}
	
	
	/**
	 * select id, name from (select * from student);
	 * @param selectData
	 * @return
	 */
	public static Map<Integer, List<Object>> executePlan(SelectExp selectData){
		SelectClause selectClause = selectData.select_stmt;	
		FromClause fromClause = selectData.from_stmt;
		//WhereClause whereClause = selectData.where_stmt;
		//OrderClause orderClause = selectData.order_stmt;
		
		//step 1: get the result set
		Map<Integer, List<Object>> resultSet = new HashMap<Integer, List<Object>>();
		if(fromClause.type == 1){
			//table name
			
		} else if(fromClause.type == 3){
			//selectExp
			resultSet = createPlan(fromClause.exp);
		} else {
			//alias table name
		}
		
		
		// step 2 : get all fields
		if(selectClause.str != null){
			//TODO
			if(selectClause.str.equals("TIMES")){
				return resultSet;
			}
		} else {
			//attribute list
			List<String> fieldList = new ArrayList<String>();
			while(selectClause.attr_list != null){
				fieldList.add(selectClause.attr_list.attr.attr.name);
				selectClause.attr_list = selectClause.attr_list.next;
			}
			//project
			return resultSet;
		}
		
		return resultSet;
	}
	
	
	
	
	/**
	 * a simple select impletetion, but only one table
	 * select id, name from student where id=155;
	 * @param selectData
	 */
	public static Map<Integer, List<Object>> createPlan(SelectExp selectData){
		SelectClause selectClause = selectData.select_stmt;	
		FromClause fromClause = selectData.from_stmt;
		WhereClause whereClause = selectData.where_stmt;
		//OrderClause orderClause = selectData.order_stmt;

		String tableName = fromClause.tableList.attr.attr.name;
		RecordPage recordPage = TableManager.getTableRecordPage(tableName);
		if(recordPage == null) return null;
		// step 2 : get all fields
		List<String> fieldList = new ArrayList<String>();
		if(selectClause.str != null){
			//TODO
			if(selectClause.str.equals("TIMES")){
				for(String field : recordPage.table.getTableSchema().getInfo().keySet()){
					fieldList.add(field);
				}
			}
			// add all table's field to fieldList
		} else {
			//attribute list
			while(selectClause.attr_list != null){
				fieldList.add(selectClause.attr_list.attr.attr.name);
				selectClause.attr_list = selectClause.attr_list.next;
			}
		}
		
		//step 5 : check the field and get all data
		//RecordPage recordPage = new RecordPage(buffer, table, 0);
		Map<Integer, List<Object>> data = recordPage.select(fieldList);
		
		//step 6 : analysis condition
		//this list is for avoid ConcurrencyModificationException
		List<Integer> removeItem = new ArrayList<Integer>();
		if(whereClause != null){
			ConditionList conditionList = whereClause.condition_list;
			for(Integer integer : data.keySet()){
				if(!conditionList.satisfy(fieldList, data.get(integer))){
					removeItem.add(integer);
					/*//System.out.print(integer + Constants.DBMS_IFN);
					for(Object object : data.get(integer)){
						System.out.print(object + Constants.DBMS_IFN);
					}
					System.out.println();*/
				}
			}
		}
		
		for(Integer integer : removeItem){
			data.remove(integer);
		}
		
		return data;
	}
}
